#ifndef __UDP_SERVER_H__
#define __UDP_SERVER_H__

#include <cstdint>
#include <string>
#include <stdexcept>
#include <cstring>
#include <thread>
#include <vector>
#include <functional>

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/epoll.h>
#endif

namespace LiteNetLib
{

#ifdef _WIN32
    typedef struct
    {
        WSAOVERLAPPED overlapped;
        WSABUF wsabuf;
        char buffer[2048];
        sockaddr_in remoteAddr;
        int remoteAddrLen;
        int operation;
    } IO_DATA;
#endif


    class UdpServer
    {
    private:
        std::string m_ip;
        int m_port;
        int m_sockfd = -1;
        bool m_isRunning = false;
        std::thread *m_receiveThread;

#ifdef _WIN32
        WSADATA m_wsaData;
        HANDLE m_iocpPort;
        IO_DATA m_ioData;
#else
        char recvBuffer[2048];
#endif

        std::string m_address;

    private:
        void receiveData();

    public:
        std::function<void(char *, uint32_t, struct sockaddr *)> onMessage;

    public:
        UdpServer();
        ~UdpServer();

        static void getAddress(struct sockaddr *client_addr, std::string &outAddress);
        static void getIpStr(struct sockaddr *client_addr, std::string &outIpStr);
        static int getPort(struct sockaddr *client_addr);

        inline const std::string &getBindAddress() const;

        int create(int port, const char *host = "0.0.0.0");
        void start();
        void stop();
        inline bool isRunning() const;
        void sendData(const void *data, int size, struct sockaddr *client_addr);
    };

    inline bool UdpServer::isRunning() const
    {
        return m_isRunning;
    }

    inline const std::string &UdpServer::getBindAddress() const
    {
        return m_address;
    }
}

#endif
